#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "QueueNetworkModel.h"
//#include "request.h"

//compute and get hardware parameter

#define MEGA 1000000.0

double getCpuServiceTime(){
	return (1.0 / CPU_SERVICE_RATE);
}

double getWebSwitchServiceTime(){
	return (1.0 / WEBSWITCH_SERVICE_RATE);
}

double getNetworkServiceTime(int messageSize, double bandwidth){
	int nDatagrams = ceil(((double)messageSize) / MSS);
	int overhead = nDatagrams * (TCP_OVERHEAD + IP_OVERHEAD + FRAME_OVERHEAD);
	double st = (messageSize + overhead) * 8;
	st = st / (bandwidth * MEGA);
	return st;
}

//controllare i rapporti dimensionali
double getDiskServiceTime(int fileSize){
	double revolutionTime = (double)60 / DISK_SPEED;

        double rotationalLatency = revolutionTime * 0.5;
	double transferTime = DISK_BLOCK_SIZE / (DISK_TRANSFER_RATE * MEGA);
	double st = DISK_CONTROLLER_TIME + DISK_SEEK_TIME + rotationalLatency + transferTime;

	int nBlocks = ceil(((double)fileSize) / DISK_BLOCK_SIZE);
	st = st * (double)nBlocks;
	return st;
}

//Use Facility

ServerLocation getServerLocation(int index){
    ServerLocation sl;
    sl.server = index & 0x0000001F;
    sl.cluster = index>>5;
    if (sl.cluster == 1) sl.server -= 1;
    if (sl.cluster == 3) sl.server -= 2;
    return sl;
}

void useServerFacility(ServerFacility * sf, PartitionedRequest * pr){
    use(sf->cpu, getCpuServiceTime());

    use(sf->disk, getDiskServiceTime(pr->size));

    use(sf->cpu, getCpuServiceTime());
}

void useLocalAreaFacility(LocalAreaFacility * lf, PartitionedRequest * pr){
    double start_time = simtime();

    ServerLocation sl = getServerLocation((int)(pr->server));
    use(lf->web_switch, getWebSwitchServiceTime());
    use(lf->lan, getNetworkServiceTime(AVG_SIZE_HTTP_REQUEST, LAN_BANDWIDTH));

    useServerFacility(lf->servers[sl.server], pr);
    
    use(lf->lan, getNetworkServiceTime(pr->size+AVG_SIZE_HTTP_RESPONSE, LAN_BANDWIDTH));
    use(lf->web_switch, getWebSwitchServiceTime());

    double local_area_response_time = simtime() - start_time;
    tabulate(lf->local_area_response_time, local_area_response_time);

#if (STOCHASTIC_SIMULATION) && (STOCHASTIC_SIZE) && (STOCHASTIC_CLUSTERIZED_SIZE)
    tabulate(lf->local_area_class_response_time[pr->p_class], local_area_response_time);
#else
    tabulate(lf->local_area_class_response_time[pr->p_class], local_area_response_time);
#endif
}

void useNetworkFacility(PartitionedRequest *pr){
    double start_time = simtime();

    use(network_facility->link_cdd, getNetworkServiceTime(AVG_SIZE_HTTP_REQUEST, GENERIC_BANDWIDTH) + 3*getNetworkServiceTime(0.00001, GENERIC_BANDWIDTH));
    hold(CDD_SERVICE_TIME);
    use(network_facility->link_in, getNetworkServiceTime(AVG_SIZE_HTTP_REQUEST, GENERIC_BANDWIDTH));
    ServerLocation sl = getServerLocation((int)(pr->server));
    useLocalAreaFacility(network_facility->local_areas[sl.cluster], pr);
    use(network_facility->link_out, getNetworkServiceTime(AVG_SIZE_HTTP_RESPONSE, GENERIC_BANDWIDTH) + 2*getNetworkServiceTime(0.00001, GENERIC_BANDWIDTH));

    double network_response_time = simtime() - start_time;
    tabulate(network_facility->network_response_time, network_response_time);

#if (STOCHASTIC_SIMULATION) && (STOCHASTIC_SIZE) && (STOCHASTIC_CLUSTERIZED_SIZE)
    tabulate(network_facility->network_class_response_time[pr->p_class], network_response_time);
#else
    tabulate(network_facility->network_class_response_time[pr->p_class], network_response_time);
#endif

#if RECORD_OSSERVATION
    recordObservation(network_response_time);
#endif
    
}

//Model Statistics

void computeServerStatistics(ServerFacility * sf){
    sf->cpu_server_index = (FacilityPerformanceIndex*)malloc(sizeof(FacilityPerformanceIndex));
    sf->disk_server_index = (FacilityPerformanceIndex*)malloc(sizeof(FacilityPerformanceIndex));

    sf->cpu_server_index->queue_len = qlen(sf->cpu);
    sf->cpu_server_index->response_time = resp(sf->cpu);
    sf->cpu_server_index->service_time = serv(sf->cpu);
    sf->cpu_server_index->throughput = tput(sf->cpu);
    sf->cpu_server_index->utilization = util(sf->cpu);
    sf->cpu_server_index->completion = (double)completions(sf->cpu);

    sf->disk_server_index->queue_len = qlen(sf->disk);
    sf->disk_server_index->response_time = resp(sf->disk);
    sf->disk_server_index->service_time = serv(sf->disk);
    sf->disk_server_index->throughput = tput(sf->disk);
    sf->disk_server_index->utilization = util(sf->disk);
    sf->disk_server_index->completion = (double)completions(sf->disk);

}

void computeLocalAreaStatistics(LocalAreaFacility * la){
    la->lan_index = (FacilityPerformanceIndex*)malloc(sizeof(FacilityPerformanceIndex));
    la->web_switch_index = (FacilityPerformanceIndex*)malloc(sizeof(FacilityPerformanceIndex));

    la->cpu_local_area_avg_index = (FacilityPerformanceIndex*)malloc(sizeof(FacilityPerformanceIndex));
    la->disk_local_area_avg_index = (FacilityPerformanceIndex*)malloc(sizeof(FacilityPerformanceIndex));
    memset(la->cpu_local_area_avg_index, 0, sizeof(FacilityPerformanceIndex));
    memset(la->disk_local_area_avg_index, 0, sizeof(FacilityPerformanceIndex));


    la->lan_index->queue_len = qlen(la->lan);
    la->lan_index->response_time = resp(la->lan);
    la->lan_index->service_time = serv(la->lan);
    la->lan_index->throughput = tput(la->lan);
    la->lan_index->utilization = util(la->lan);
    la->lan_index->completion = (double)completions(la->lan);

    la->web_switch_index->queue_len = qlen(la->web_switch);
    la->web_switch_index->response_time = resp(la->web_switch);
    la->web_switch_index->service_time = serv(la->web_switch);
    la->web_switch_index->throughput = tput(la->web_switch);
    la->web_switch_index->utilization = util(la->web_switch);
    la->web_switch_index->completion = (double)completions(la->web_switch);

    int i;
    for(i=0; i<la->n_servers; i++){
        computeServerStatistics(la->servers[i]);

        la->cpu_local_area_avg_index->queue_len += la->servers[i]->cpu_server_index->queue_len;
        la->cpu_local_area_avg_index->response_time += la->servers[i]->cpu_server_index->response_time;
        la->cpu_local_area_avg_index->service_time += la->servers[i]->cpu_server_index->service_time;
        la->cpu_local_area_avg_index->throughput += la->servers[i]->cpu_server_index->throughput;
        la->cpu_local_area_avg_index->utilization += la->servers[i]->cpu_server_index->utilization;
        la->cpu_local_area_avg_index->completion += la->servers[i]->cpu_server_index->completion;

        la->disk_local_area_avg_index->queue_len += la->servers[i]->disk_server_index->queue_len;
        la->disk_local_area_avg_index->response_time += la->servers[i]->disk_server_index->response_time;
        la->disk_local_area_avg_index->service_time += la->servers[i]->disk_server_index->service_time;
        la->disk_local_area_avg_index->throughput += la->servers[i]->disk_server_index->throughput;
        la->disk_local_area_avg_index->utilization += la->servers[i]->disk_server_index->utilization;
        la->disk_local_area_avg_index->completion += la->servers[i]->disk_server_index->completion;
    }

    la->cpu_local_area_avg_index->queue_len /= la->n_servers;
    la->cpu_local_area_avg_index->response_time /= la->n_servers;
    la->cpu_local_area_avg_index->service_time /= la->n_servers;
    la->cpu_local_area_avg_index->throughput /= la->n_servers;
    la->cpu_local_area_avg_index->utilization /= la->n_servers;
    la->cpu_local_area_avg_index->completion /= la->n_servers;

    la->disk_local_area_avg_index->queue_len /= la->n_servers;
    la->disk_local_area_avg_index->response_time /= la->n_servers;
    la->disk_local_area_avg_index->service_time /= la->n_servers;
    la->disk_local_area_avg_index->throughput /= la->n_servers;
    la->disk_local_area_avg_index->utilization /= la->n_servers;
    la->disk_local_area_avg_index->completion /= la->n_servers;

}

void computeNetworkStatistics(){
    NetworkFacility * nf = network_facility;
    nf->link_cdd_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));
    nf->link_out_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));
    nf->link_in_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));
    nf->web_switch_network_avg_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));
    nf->lan_network_avg_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));
    nf->cpu_network_avg_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));
    nf->disk_network_avg_index = (FacilityPerformanceIndex*) malloc(sizeof(FacilityPerformanceIndex));

    memset(nf->web_switch_network_avg_index, 0, sizeof(FacilityPerformanceIndex));
    memset(nf->lan_network_avg_index, 0, sizeof(FacilityPerformanceIndex));
    memset(nf->cpu_network_avg_index, 0, sizeof(FacilityPerformanceIndex));
    memset(nf->disk_network_avg_index, 0, sizeof(FacilityPerformanceIndex));

    nf->link_cdd_index->queue_len = qlen(nf->link_cdd);
    nf->link_cdd_index->response_time = resp(nf->link_cdd);
    nf->link_cdd_index->throughput = tput(nf->link_cdd);
    nf->link_cdd_index->utilization = util(nf->link_cdd);
    nf->link_cdd_index->service_time = serv(nf->link_cdd);
    nf->link_cdd_index->completion = (double)completions(nf->link_cdd);

    nf->link_out_index->queue_len = qlen(nf->link_out);
    nf->link_out_index->response_time = resp(nf->link_out);
    nf->link_out_index->throughput = tput(nf->link_out);
    nf->link_out_index->utilization = util(nf->link_out);
    nf->link_out_index->service_time = serv(nf->link_out);
    nf->link_out_index->completion = (double)completions(nf->link_out);

    nf->link_in_index->queue_len = qlen(nf->link_in);
    nf->link_in_index->response_time = resp(nf->link_in);
    nf->link_in_index->throughput = tput(nf->link_in);
    nf->link_in_index->utilization = util(nf->link_in);
    nf->link_in_index->service_time = serv(nf->link_in);
    nf->link_in_index->completion = (double)completions(nf->link_in);

    int i;
    for(i=0; i<nf->n_loc_areas; i++){
        computeLocalAreaStatistics(nf->local_areas[i]);

        nf->web_switch_network_avg_index->queue_len += nf->local_areas[i]->web_switch_index->queue_len;
        nf->web_switch_network_avg_index->response_time += nf->local_areas[i]->web_switch_index->response_time;
        nf->web_switch_network_avg_index->service_time += nf->local_areas[i]->web_switch_index->service_time;
        nf->web_switch_network_avg_index->throughput += nf->local_areas[i]->web_switch_index->throughput;
        nf->web_switch_network_avg_index->utilization += nf->local_areas[i]->web_switch_index->utilization;
        nf->web_switch_network_avg_index->completion += nf->local_areas[i]->web_switch_index->completion;

        nf->lan_network_avg_index->queue_len += nf->local_areas[i]->lan_index->queue_len;
        nf->lan_network_avg_index->response_time += nf->local_areas[i]->lan_index->response_time;
        nf->lan_network_avg_index->service_time += nf->local_areas[i]->lan_index->service_time;
        nf->lan_network_avg_index->throughput += nf->local_areas[i]->lan_index->throughput;
        nf->lan_network_avg_index->utilization += nf->local_areas[i]->lan_index->utilization;
        nf->lan_network_avg_index->completion += nf->local_areas[i]->lan_index->completion;

        nf->cpu_network_avg_index->queue_len += nf->local_areas[i]->cpu_local_area_avg_index->queue_len;
        nf->cpu_network_avg_index->response_time += nf->local_areas[i]->cpu_local_area_avg_index->response_time;
        nf->cpu_network_avg_index->service_time += nf->local_areas[i]->cpu_local_area_avg_index->service_time;
        nf->cpu_network_avg_index->throughput += nf->local_areas[i]->cpu_local_area_avg_index->throughput;
        nf->cpu_network_avg_index->utilization += nf->local_areas[i]->cpu_local_area_avg_index->utilization;
        nf->cpu_network_avg_index->completion += nf->local_areas[i]->cpu_local_area_avg_index->completion;

        nf->disk_network_avg_index->queue_len += nf->local_areas[i]->disk_local_area_avg_index->queue_len;
        nf->disk_network_avg_index->response_time += nf->local_areas[i]->disk_local_area_avg_index->response_time;
        nf->disk_network_avg_index->service_time += nf->local_areas[i]->disk_local_area_avg_index->service_time;
        nf->disk_network_avg_index->throughput += nf->local_areas[i]->disk_local_area_avg_index->throughput;
        nf->disk_network_avg_index->utilization += nf->local_areas[i]->disk_local_area_avg_index->utilization;
        nf->disk_network_avg_index->completion += nf->local_areas[i]->disk_local_area_avg_index->completion;
    }

    nf->web_switch_network_avg_index->queue_len /= nf->n_loc_areas;
    nf->web_switch_network_avg_index->response_time /= nf->n_loc_areas;
    nf->web_switch_network_avg_index->service_time /= nf->n_loc_areas;
    nf->web_switch_network_avg_index->throughput /= nf->n_loc_areas;
    nf->web_switch_network_avg_index->utilization /= nf->n_loc_areas;
    nf->web_switch_network_avg_index->completion /= nf->n_loc_areas;

    nf->lan_network_avg_index->queue_len /= nf->n_loc_areas;
    nf->lan_network_avg_index->response_time /= nf->n_loc_areas;
    nf->lan_network_avg_index->service_time /= nf->n_loc_areas;
    nf->lan_network_avg_index->throughput /= nf->n_loc_areas;
    nf->lan_network_avg_index->utilization /= nf->n_loc_areas;
    nf->lan_network_avg_index->completion /= nf->n_loc_areas;

    nf->cpu_network_avg_index->queue_len /= nf->n_loc_areas;
    nf->cpu_network_avg_index->response_time /= nf->n_loc_areas;
    nf->cpu_network_avg_index->service_time /= nf->n_loc_areas;
    nf->cpu_network_avg_index->throughput /= nf->n_loc_areas;
    nf->cpu_network_avg_index->utilization /= nf->n_loc_areas;
    nf->cpu_network_avg_index->completion /= nf->n_loc_areas;

    nf->disk_network_avg_index->queue_len /= nf->n_loc_areas;
    nf->disk_network_avg_index->response_time /= nf->n_loc_areas;
    nf->disk_network_avg_index->service_time /= nf->n_loc_areas;
    nf->disk_network_avg_index->throughput /= nf->n_loc_areas;
    nf->disk_network_avg_index->utilization /= nf->n_loc_areas;
    nf->disk_network_avg_index->completion /= nf->n_loc_areas;
}


void printLocalAreaFacilityPerformanceIndex(){
    int i;
    for(i=0; i<N_LOCAL_AREA; i++){
	printf(" ---------- %s Local Area Statistics ---------\n\n", network_facility->local_areas[i]->name );

	printf("     --------------- Response Time ---------------\n");
	printf("WebSwitch: \t%f\n", network_facility->local_areas[i]->web_switch_index->response_time);
	printf("Lan:       \t%f\n", network_facility->local_areas[i]->lan_index->response_time);
	printf("Cpu:       \t%f\n", network_facility->local_areas[i]->cpu_local_area_avg_index->response_time);
	printf("Dischi:    \t%f\n", network_facility->local_areas[i]->disk_local_area_avg_index->response_time);

	printf("     --------------- Queue Lenght ---------------\n");
	printf("WebSwitch: \t%f\n", network_facility->local_areas[i]->web_switch_index->queue_len);
	printf("Lan:       \t%f\n", network_facility->local_areas[i]->lan_index->queue_len);
	printf("Cpu:       \t%f\n", network_facility->local_areas[i]->cpu_local_area_avg_index->queue_len);
	printf("Dischi:    \t%f\n", network_facility->local_areas[i]->disk_local_area_avg_index->queue_len);

	printf("     --------------- Utilization ----------------\n");
	printf("WebSwitch: \t%f\n", network_facility->local_areas[i]->web_switch_index->utilization);
	printf("Lan:       \t%f\n", network_facility->local_areas[i]->lan_index->utilization);
	printf("Cpu:       \t%f\n", network_facility->local_areas[i]->cpu_local_area_avg_index->utilization);
	printf("Dischi:    \t%f\n", network_facility->local_areas[i]->disk_local_area_avg_index->utilization);

	printf("     --------------- Service Time ----------------\n");
	printf("WebSwitch: \t%f\n", network_facility->local_areas[i]->web_switch_index->service_time);
	printf("Lan:       \t%f\n", network_facility->local_areas[i]->lan_index->service_time);
	printf("Cpu:       \t%f\n", network_facility->local_areas[i]->cpu_local_area_avg_index->service_time);
	printf("Dischi:    \t%f\n", network_facility->local_areas[i]->disk_local_area_avg_index->service_time);

	printf("     --------------- Throughput ------------------\n");
	printf("WebSwitch: \t%f\n", network_facility->local_areas[i]->web_switch_index->throughput);
	printf("Lan:       \t%f\n", network_facility->local_areas[i]->lan_index->throughput);
	printf("Cpu:       \t%f\n", network_facility->local_areas[i]->cpu_local_area_avg_index->throughput);
	printf("Dischi:    \t%f\n", network_facility->local_areas[i]->disk_local_area_avg_index->throughput);

        printf("     --------------- Completion ------------------\n");
	printf("WebSwitch: \t%f\n", network_facility->local_areas[i]->web_switch_index->completion);
	printf("Lan:       \t%f\n", network_facility->local_areas[i]->lan_index->completion);
	printf("Cpu:       \t%f\n", network_facility->local_areas[i]->cpu_local_area_avg_index->completion);
	printf("Dischi:    \t%f\n\n\n", network_facility->local_areas[i]->disk_local_area_avg_index->completion);
    }
}

void printNetworkFacilityPerformanceIndex() {
    printf(" --------------- Network Statistics ----------------\n\n");

    printf("     --------------- Response Time ---------------\n");
    printf("WebSwitch: \t%f\n", network_facility->web_switch_network_avg_index->response_time);
    printf("Lan:       \t%f\n", network_facility->lan_network_avg_index->response_time);
    printf("Cpu:       \t%f\n", network_facility->cpu_network_avg_index->response_time);
    printf("Dischi:    \t%f\n", network_facility->disk_network_avg_index->response_time);
    printf("LinkToCDD: \t%f\n", network_facility->link_cdd_index->response_time);
    printf("LinkIn:    \t%f\n", network_facility->link_in_index->response_time);
    printf("LinkOut:   \t%f\n", network_facility->link_out_index->response_time);

    printf("     --------------- Queue Lenght ---------------\n");
    printf("WebSwitch: \t%f\n", network_facility->web_switch_network_avg_index->queue_len);
    printf("Lan:       \t%f\n", network_facility->lan_network_avg_index->queue_len);
    printf("Cpu:       \t%f\n", network_facility->cpu_network_avg_index->queue_len);
    printf("Dischi:    \t%f\n", network_facility->disk_network_avg_index->queue_len);
    printf("LinkToCDD: \t%f\n", network_facility->link_cdd_index->queue_len);
    printf("LinkIn:    \t%f\n", network_facility->link_in_index->queue_len);
    printf("LinkOut:   \t%f\n", network_facility->link_out_index->queue_len);

    printf("     --------------- Utilization ----------------\n");
    printf("WebSwitch: \t%f\n", network_facility->web_switch_network_avg_index->utilization);
    printf("Lan:       \t%f\n", network_facility->lan_network_avg_index->utilization);
    printf("Cpu:       \t%f\n", network_facility->cpu_network_avg_index->utilization);
    printf("Dischi:    \t%f\n", network_facility->disk_network_avg_index->utilization);
    printf("LinkToCDD: \t%f\n", network_facility->link_cdd_index->utilization);
    printf("LinkIn:    \t%f\n", network_facility->link_in_index->utilization);
    printf("LinkOut:   \t%f\n", network_facility->link_out_index->utilization);

    printf("     --------------- Service Time ----------------\n");
    printf("WebSwitch: \t%f\n", network_facility->web_switch_network_avg_index->service_time);
    printf("Lan:       \t%f\n", network_facility->lan_network_avg_index->service_time);
    printf("Cpu:       \t%f\n", network_facility->cpu_network_avg_index->service_time);
    printf("Dischi:    \t%f\n", network_facility->disk_network_avg_index->service_time);
    printf("LinkToCDD: \t%f\n", network_facility->link_cdd_index->service_time);
    printf("LinkIn:    \t%f\n", network_facility->link_in_index->service_time);
    printf("LinkOut:   \t%f\n", network_facility->link_out_index->service_time);

    printf("     --------------- Throughput ------------------\n");
    printf("WebSwitch: \t%f\n", network_facility->web_switch_network_avg_index->throughput);
    printf("Lan:       \t%f\n", network_facility->lan_network_avg_index->throughput);
    printf("Cpu:       \t%f\n", network_facility->cpu_network_avg_index->throughput);
    printf("Dischi:    \t%f\n", network_facility->disk_network_avg_index->throughput);
    printf("LinkToCDD: \t%f\n", network_facility->link_cdd_index->throughput);
    printf("LinkIn:    \t%f\n", network_facility->link_in_index->throughput);
    printf("LinkOut:   \t%f\n", network_facility->link_out_index->throughput);

    printf("     --------------- Completion ------------------\n");
    printf("WebSwitch: \t%f\n", network_facility->web_switch_network_avg_index->completion);
    printf("Lan:       \t%f\n", network_facility->lan_network_avg_index->completion);
    printf("Cpu:       \t%f\n", network_facility->cpu_network_avg_index->completion);
    printf("Dischi:    \t%f\n", network_facility->disk_network_avg_index->completion);
    printf("LinkToCDD: \t%f\n", network_facility->link_cdd_index->completion);
    printf("LinkIn:    \t%f\n", network_facility->link_in_index->completion);
    printf("LinkOut:   \t%f\n\n\n", network_facility->link_out_index->completion);
}